#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 
#include <ctype.h> 
#include <time.h> 
#include <pthread.h> 
#include <getopt.h> 
#include <libgen.h> 
#include <sys/types.h>   
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <sys/epoll.h> 
#include <sys/resource.h>


#define MAX_EVENTS 	512
#define MSG_STR		"Hello, you have connected to the server successfully!\n"

void set_socket_rlimit();

void set_socket_rlimit()
{
	struct rlimit limit = {0};

	getrlimit(RLIMIT_NOFILE, &limit);
	limit.rlim_cur = limit.rlim_max;
	setrlimit(RLIMIT_NOFILE, &limit);

	printf("set socket open fd max count to %d\n", limit.rlim_max);
}


int main(int argc, char **argv)
{
	int					listen_fd = -1, connect_fd = -1;
	int					listen_port = 8889;
	int					daemon_run = 0;
	int					i, j;
	char					buf[1024];
	struct sockaddr_in			server_addr;
	int 					rv = 0;
	int 					on = 1;


	int					epoll_fd;
	struct epoll_event			event;
	struct epoll_event			event_array[MAX_EVENTS];
	int					events;

	if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("socket() creates a TCP socket unsuccessfully : %s", strerror(errno));
		return -1;
	}

	
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(listen_port);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));


	if(bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
	{
		printf("bind() binds the TCP socket unsuccessfully : %s, strerror(errno)");
		return -2;
	}

	if(listen(listen_fd, 64) < 0)
	{
		printf("listen() listens the port unsuccessfully : %s, strerror(errno)");
		return -3;
	}

	set_socket_rlimit();

	if ( daemon_run )
	{
		daemon(0, 0);
	}

	if ((epoll_fd = epoll_create(MAX_EVENTS)) < 0)
	{
		printf("epoll_create() creates epoll_fd unsuccessfully");
		return -4;
	}

	event.events = EPOLLIN|EPOLLET;
	event.data.fd = listen_fd;

	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0)
	{
		printf("epoll add listen socket unsuccessfully : %s\n", strerror(errno));
		return -5;
	}

	while(1)
	{
		events = epoll_wait(epoll_fd, event_array, MAX_EVENTS, -1);
		if (events < 0)
		{
			printf("epoll failure : %s\n", strerror(errno));
			break;
		}
		else if (events == 0)
		{
			printf("epoll get timeout\n");
			continue;
		}
		
		for(i = 0; i < events; i++)
		{
			if((event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP))
			{
				printf("event_array[i] get error on fd[%d] : %s\n", event_array[i].data.fd, strerror(errno));
				epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
				close(event_array[i].data.fd);
			}

			if(event_array[i].data.fd == listen_fd)
			{
				if((connect_fd = accept(listen_fd, (struct sockaddr *) NULL, NULL)) < 0)
				{
					printf("accept new client unsuccessfully : %s\n", strerror(errno));
					continue;
				}

				event.data.fd = connect_fd;
				event.events = EPOLLIN|EPOLLET;

				if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connect_fd, &event) < 0)
				{
					printf("epoll add client socket unsuccessfully : %s\n", strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				printf("epoll add new client socket[%d] successfully!\n\n", connect_fd);
			}

			else
			{	
				memset(buf, 0, sizeof(buf));

				if((rv = read(event_array[i].data.fd, buf, sizeof(buf))) <=0)
				{
					printf("socket[%d] read unsuccessfully!\n", event_array[i].data.fd);
					epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
					close(event_array[i].data.fd);
					continue;
				}
				else
				{
					printf("socket[%d] read %d bytes data\n%s\n", event_array[i].data.fd, rv, buf);
					 if( write(event_array[i].data.fd, MSG_STR, strlen(MSG_STR)) < 0 )
		     			 {                        
						printf("socket[%d] write failure: %s\n", event_array[i].data.fd, strerror(errno)); 
			 			epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);    
			    			close(event_array[i].data.fd);    
			 		 } 
				}
			}
		}
	}
	close(listen_fd);
	return 0;
}
